#include	"CObject.h"
#include	<iostream>
#include	<fstream>
#include	<algorithm>

CObject::CObject(Config & config, const std::string & name)
	:	m_config(&config)
	,	m_name(name)
	,	m_material()
	,	m_triangle()
{
}

CObject::~CObject()
{
	Cleanup();
}

void CObject::Cleanup()
{
	m_name.clear();
	m_material.clear();
	m_triangle.clear();
}

void CObject::AddMaterial(const CMaterial & material)
{
	m_material.push_back(material);
}

void CObject::AddTriangle(const CTriangle & triangle)
{
	m_triangle.push_back(triangle);
}

namespace
{

	struct Point
	{
		float x, y, z;
	};

	Point Center(const CTriangle & t)
	{
		Point p;
		p.x = (t.p1.x + t.p2.x + t.p3.x) / 3;
		p.y = (t.p1.y + t.p2.y + t.p3.y) / 3;
		p.z = (t.p1.z + t.p2.z + t.p3.z) / 3;
		return p;
	}

	bool CompareTriangles(const CTriangle & t1, const CTriangle & t2)
	{
		Point c1 = Center(t1);
		Point c2 = Center(t2);
		return ((c1.z < c2.z) || ((c1.z == c2.z) && (c1.y < c2.y))
			|| ((c1.z == c2.z) && (c1.y == c2.y) && (c1.x < c2.x)));
	}

}

void CObject::SortTriangles()
{
	if(m_config->Verbose())
	{
		std::cout << "Sorting triangles..." << std::endl;
	}
	std::sort(m_triangle.begin(), m_triangle.end(), CompareTriangles);
}

void CObject::Save(const std::string & file) const
{
	if(m_config->Verbose())
	{
		std::cout << "Saving CEM: " << file << std::endl;
	}
	std::ofstream stream(file.c_str(), std::ios::out | std::ios::binary);
	if(stream)
	{
		// write header information
		WriteHeader(stream);
		// materials
		WriteMaterials(stream);
		// triangles
		WriteTriangles(stream);
		stream.close();
	}
	else
	{
		throw std::exception(std::string("Error: Can not open file \"" + file + "\".").c_str());
	}
}

namespace
{

	void Write(std::ostream & stream, unsigned int value)
	{
		stream.write(reinterpret_cast< char * >(&value), sizeof(unsigned int));
	}

	void Write(std::ostream & stream, int value)
	{
		stream.write(reinterpret_cast< char * >(&value), sizeof(int));
	}

	void Write(std::ostream & stream, float value)
	{
		stream.write(reinterpret_cast< char * >(&value), sizeof(float));
	}

	void Write(std::ostream & stream, const std::string & string)
	{
		Write(stream, string.length());
		stream.write(string.c_str(), string.length());
	}

	void Write(std::ostream & stream, const CColor & value)
	{
		Write(stream, value.r);
		Write(stream, value.g);
		Write(stream, value.b);
		Write(stream, value.a);
	}

	void Write(std::ostream & stream, const CPoint & value)
	{
		Write(stream, value.x);
		Write(stream, value.y);
		Write(stream, value.z);
		Write(stream, value.u);
		Write(stream, value.v);
	}

}

void CObject::WriteHeader(std::ostream & stream) const
{
	Write(stream, unsigned int(0x43454D4F));
	Write(stream, unsigned int(0x01000000));
	Write(stream, m_material.size());
	Write(stream, m_triangle.size());
}

void CObject::WriteMaterials(std::ostream & stream) const
{
	for(std::vector< CMaterial >::const_iterator it = m_material.begin(); it != m_material.end(); ++it)
	{
		Write(stream, (*it).shininess);
		Write(stream, (*it).diffuse);
		Write(stream, (*it).ambient);
		Write(stream, (*it).specular);
		Write(stream, (*it).emission);
		Write(stream, (*it).texture);
	}
}

void CObject::WriteTriangles(std::ostream & stream) const
{
	for(std::vector< CTriangle >::const_iterator it = m_triangle.begin();
		it != m_triangle.end(); ++it)
	{
		Write(stream, (*it).mat);
		Write(stream, (*it).p1);
		Write(stream, (*it).p2);
		Write(stream, (*it).p3);
	}
}

